{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-05-29T15:07:01.062635Z",
     "start_time": "2025-05-29T15:07:01.004035Z"
    }
   },
   "source": [
    "import geopandas as gpd\n",
    "\n",
    "gdf = gpd.read_file(\"./tokyo.json\")\n",
    "print(gdf)"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    kucode kuname  ken  ku                                           geometry\n",
      "0     1318  東京18区   13  18  POLYGON ((139.46 35.698, 139.46 35.7, 139.46 3...\n",
      "1     1324  東京24区   13  24  POLYGON ((139.38 35.692, 139.37 35.692, 139.36...\n",
      "2     1325  東京25区   13  25  POLYGON ((139.38 35.692, 139.39 35.691, 139.39...\n",
      "3     1319  東京19区   13  19  POLYGON ((139.57 35.722, 139.57 35.724, 139.57...\n",
      "4     1322  東京22区   13  22  POLYGON ((139.47 35.649, 139.47 35.647, 139.47...\n",
      "5     1323  東京23区   13  23  POLYGON ((139.27 35.609, 139.27 35.611, 139.28...\n",
      "6     1321  東京21区   13  21  POLYGON ((139.46 35.692, 139.46 35.695, 139.45...\n",
      "7     1309   東京9区   13   9  POLYGON ((139.59 35.712, 139.59 35.716, 139.59...\n",
      "8     1308   東京8区   13   8  POLYGON ((139.59 35.694, 139.59 35.691, 139.59...\n",
      "9     1320  東京20区   13  20  POLYGON ((139.36 35.74, 139.37 35.739, 139.37 ...\n",
      "10    1311  東京11区   13  11  POLYGON ((139.64 35.769, 139.64 35.769, 139.64...\n",
      "11    1305   東京5区   13   5  POLYGON ((139.68 35.603, 139.68 35.606, 139.68...\n",
      "12    1304   東京4区   13   4  MULTIPOLYGON (((139.82 35.533, 139.83 35.542, ...\n",
      "13    1310  東京10区   13  10  POLYGON ((139.62 35.73, 139.62 35.729, 139.63 ...\n",
      "14    1306   東京6区   13   6  POLYGON ((139.59 35.618, 139.59 35.618, 139.6 ...\n",
      "15    1312  東京12区   13  12  POLYGON ((139.72 35.745, 139.71 35.733, 139.72...\n",
      "16    1313  東京13区   13  13  POLYGON ((139.78 35.754, 139.79 35.753, 139.79...\n",
      "17    1307   東京7区   13   7  POLYGON ((139.66 35.672, 139.66 35.671, 139.67...\n",
      "18    1303   東京3区   13   3  MULTIPOLYGON (((139.77 35.596, 139.76 35.609, ...\n",
      "19    1317  東京17区   13  17  POLYGON ((139.82 35.743, 139.82 35.738, 139.83...\n",
      "20    1316  東京16区   13  16  POLYGON ((139.84 35.719, 139.84 35.717, 139.84...\n",
      "21    1302   東京2区   13   2  POLYGON ((139.72 35.712, 139.72 35.71, 139.73 ...\n",
      "22    1314  東京14区   13  14  POLYGON ((139.76 35.732, 139.76 35.731, 139.77...\n",
      "23    1301   東京1区   13   1  MULTIPOLYGON (((139.76 35.628, 139.76 35.634, ...\n",
      "24    1315  東京15区   13  15  MULTIPOLYGON (((139.8 35.617, 139.8 35.618, 13...\n"
     ]
    }
   ],
   "execution_count": 3
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:07:02.792341Z",
     "start_time": "2025-05-29T15:07:02.779103Z"
    }
   },
   "cell_type": "code",
   "source": [
    "def convert_multipolygon(row):\n",
    "    if row.geometry.geom_type == 'MultiPolygon':\n",
    "        # 分解MultiPolygon为多个Polygon\n",
    "        polygons = list(row.geometry.geoms)\n",
    "        # 为每个Polygon创建新行\n",
    "        new_rows = [row.copy() for _ in polygons]\n",
    "        for i, poly in enumerate(polygons):\n",
    "            new_rows[i].geometry = poly\n",
    "        return new_rows\n",
    "    return [row]"
   ],
   "id": "93c51bfd6d14383e",
   "outputs": [],
   "execution_count": 4
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:07:23.572441Z",
     "start_time": "2025-05-29T15:07:23.544907Z"
    }
   },
   "cell_type": "code",
   "source": [
    "expanded_rows = []\n",
    "for _, row in gdf.iterrows():\n",
    "    expanded_rows.extend(convert_multipolygon(row))\n",
    "    \n",
    "# 创建新的GeoDataFrame\n",
    "gdf = gpd.GeoDataFrame(expanded_rows, crs=gdf.crs).reset_index(drop=True)"
   ],
   "id": "701b980c8a316d94",
   "outputs": [],
   "execution_count": 5
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:07:44.813198Z",
     "start_time": "2025-05-29T15:07:40.271117Z"
    }
   },
   "cell_type": "code",
   "source": [
    "columns_to_drop = ['kuname', 'ken', 'ku']\n",
    "gdf = gdf.drop(columns=columns_to_drop)\n",
    "\n",
    "# 3. 重命名kucode列为tile_id\n",
    "gdf = gdf.rename(columns={'kucode': 'tile_id'})\n"
   ],
   "id": "776438a4a511999f",
   "outputs": [
    {
     "ename": "KeyError",
     "evalue": "\"['kuname', 'ken', 'ku'] not found in axis\"",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mKeyError\u001B[0m                                  Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[7], line 2\u001B[0m\n\u001B[0;32m      1\u001B[0m columns_to_drop \u001B[38;5;241m=\u001B[39m [\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mkuname\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mken\u001B[39m\u001B[38;5;124m'\u001B[39m, \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mku\u001B[39m\u001B[38;5;124m'\u001B[39m]\n\u001B[1;32m----> 2\u001B[0m gdf \u001B[38;5;241m=\u001B[39m \u001B[43mgdf\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdrop\u001B[49m\u001B[43m(\u001B[49m\u001B[43mcolumns\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcolumns_to_drop\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m      4\u001B[0m \u001B[38;5;66;03m# 3. 重命名kucode列为tile_id\u001B[39;00m\n\u001B[0;32m      5\u001B[0m gdf \u001B[38;5;241m=\u001B[39m gdf\u001B[38;5;241m.\u001B[39mrename(columns\u001B[38;5;241m=\u001B[39m{\u001B[38;5;124m'\u001B[39m\u001B[38;5;124mkucode\u001B[39m\u001B[38;5;124m'\u001B[39m: \u001B[38;5;124m'\u001B[39m\u001B[38;5;124mtile_id\u001B[39m\u001B[38;5;124m'\u001B[39m})\n",
      "File \u001B[1;32mD:\\Environment\\Envs\\python3.10.6\\lib\\site-packages\\pandas\\core\\frame.py:5581\u001B[0m, in \u001B[0;36mDataFrame.drop\u001B[1;34m(self, labels, axis, index, columns, level, inplace, errors)\u001B[0m\n\u001B[0;32m   5433\u001B[0m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21mdrop\u001B[39m(\n\u001B[0;32m   5434\u001B[0m     \u001B[38;5;28mself\u001B[39m,\n\u001B[0;32m   5435\u001B[0m     labels: IndexLabel \u001B[38;5;241m|\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m \u001B[38;5;241m=\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m,\n\u001B[1;32m   (...)\u001B[0m\n\u001B[0;32m   5442\u001B[0m     errors: IgnoreRaise \u001B[38;5;241m=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mraise\u001B[39m\u001B[38;5;124m\"\u001B[39m,\n\u001B[0;32m   5443\u001B[0m ) \u001B[38;5;241m-\u001B[39m\u001B[38;5;241m>\u001B[39m DataFrame \u001B[38;5;241m|\u001B[39m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[0;32m   5444\u001B[0m \u001B[38;5;250m    \u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[0;32m   5445\u001B[0m \u001B[38;5;124;03m    Drop specified labels from rows or columns.\u001B[39;00m\n\u001B[0;32m   5446\u001B[0m \n\u001B[1;32m   (...)\u001B[0m\n\u001B[0;32m   5579\u001B[0m \u001B[38;5;124;03m            weight  1.0     0.8\u001B[39;00m\n\u001B[0;32m   5580\u001B[0m \u001B[38;5;124;03m    \"\"\"\u001B[39;00m\n\u001B[1;32m-> 5581\u001B[0m     \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28;43msuper\u001B[39;49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdrop\u001B[49m\u001B[43m(\u001B[49m\n\u001B[0;32m   5582\u001B[0m \u001B[43m        \u001B[49m\u001B[43mlabels\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mlabels\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5583\u001B[0m \u001B[43m        \u001B[49m\u001B[43maxis\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43maxis\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5584\u001B[0m \u001B[43m        \u001B[49m\u001B[43mindex\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mindex\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5585\u001B[0m \u001B[43m        \u001B[49m\u001B[43mcolumns\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mcolumns\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5586\u001B[0m \u001B[43m        \u001B[49m\u001B[43mlevel\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mlevel\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5587\u001B[0m \u001B[43m        \u001B[49m\u001B[43minplace\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43minplace\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5588\u001B[0m \u001B[43m        \u001B[49m\u001B[43merrors\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43merrors\u001B[49m\u001B[43m,\u001B[49m\n\u001B[0;32m   5589\u001B[0m \u001B[43m    \u001B[49m\u001B[43m)\u001B[49m\n",
      "File \u001B[1;32mD:\\Environment\\Envs\\python3.10.6\\lib\\site-packages\\pandas\\core\\generic.py:4788\u001B[0m, in \u001B[0;36mNDFrame.drop\u001B[1;34m(self, labels, axis, index, columns, level, inplace, errors)\u001B[0m\n\u001B[0;32m   4786\u001B[0m \u001B[38;5;28;01mfor\u001B[39;00m axis, labels \u001B[38;5;129;01min\u001B[39;00m axes\u001B[38;5;241m.\u001B[39mitems():\n\u001B[0;32m   4787\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m labels \u001B[38;5;129;01mis\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m:\n\u001B[1;32m-> 4788\u001B[0m         obj \u001B[38;5;241m=\u001B[39m \u001B[43mobj\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43m_drop_axis\u001B[49m\u001B[43m(\u001B[49m\u001B[43mlabels\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43maxis\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43mlevel\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43mlevel\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43merrors\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43merrors\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m   4790\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m inplace:\n\u001B[0;32m   4791\u001B[0m     \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39m_update_inplace(obj)\n",
      "File \u001B[1;32mD:\\Environment\\Envs\\python3.10.6\\lib\\site-packages\\pandas\\core\\generic.py:4830\u001B[0m, in \u001B[0;36mNDFrame._drop_axis\u001B[1;34m(self, labels, axis, level, errors, only_slice)\u001B[0m\n\u001B[0;32m   4828\u001B[0m         new_axis \u001B[38;5;241m=\u001B[39m axis\u001B[38;5;241m.\u001B[39mdrop(labels, level\u001B[38;5;241m=\u001B[39mlevel, errors\u001B[38;5;241m=\u001B[39merrors)\n\u001B[0;32m   4829\u001B[0m     \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[1;32m-> 4830\u001B[0m         new_axis \u001B[38;5;241m=\u001B[39m \u001B[43maxis\u001B[49m\u001B[38;5;241;43m.\u001B[39;49m\u001B[43mdrop\u001B[49m\u001B[43m(\u001B[49m\u001B[43mlabels\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43merrors\u001B[49m\u001B[38;5;241;43m=\u001B[39;49m\u001B[43merrors\u001B[49m\u001B[43m)\u001B[49m\n\u001B[0;32m   4831\u001B[0m     indexer \u001B[38;5;241m=\u001B[39m axis\u001B[38;5;241m.\u001B[39mget_indexer(new_axis)\n\u001B[0;32m   4833\u001B[0m \u001B[38;5;66;03m# Case for non-unique axis\u001B[39;00m\n\u001B[0;32m   4834\u001B[0m \u001B[38;5;28;01melse\u001B[39;00m:\n",
      "File \u001B[1;32mD:\\Environment\\Envs\\python3.10.6\\lib\\site-packages\\pandas\\core\\indexes\\base.py:7070\u001B[0m, in \u001B[0;36mIndex.drop\u001B[1;34m(self, labels, errors)\u001B[0m\n\u001B[0;32m   7068\u001B[0m \u001B[38;5;28;01mif\u001B[39;00m mask\u001B[38;5;241m.\u001B[39many():\n\u001B[0;32m   7069\u001B[0m     \u001B[38;5;28;01mif\u001B[39;00m errors \u001B[38;5;241m!=\u001B[39m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mignore\u001B[39m\u001B[38;5;124m\"\u001B[39m:\n\u001B[1;32m-> 7070\u001B[0m         \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mKeyError\u001B[39;00m(\u001B[38;5;124mf\u001B[39m\u001B[38;5;124m\"\u001B[39m\u001B[38;5;132;01m{\u001B[39;00mlabels[mask]\u001B[38;5;241m.\u001B[39mtolist()\u001B[38;5;132;01m}\u001B[39;00m\u001B[38;5;124m not found in axis\u001B[39m\u001B[38;5;124m\"\u001B[39m)\n\u001B[0;32m   7071\u001B[0m     indexer \u001B[38;5;241m=\u001B[39m indexer[\u001B[38;5;241m~\u001B[39mmask]\n\u001B[0;32m   7072\u001B[0m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[38;5;28mself\u001B[39m\u001B[38;5;241m.\u001B[39mdelete(indexer)\n",
      "\u001B[1;31mKeyError\u001B[0m: \"['kuname', 'ken', 'ku'] not found in axis\""
     ]
    }
   ],
   "execution_count": 7
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:08:09.133397Z",
     "start_time": "2025-05-29T15:08:09.113496Z"
    }
   },
   "cell_type": "code",
   "source": "print(gdf)",
   "id": "3caec1f91800272c",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    tile_id                                           geometry\n",
      "0      1318  POLYGON ((139.46 35.698, 139.46 35.7, 139.46 3...\n",
      "1      1324  POLYGON ((139.38 35.692, 139.37 35.692, 139.36...\n",
      "2      1325  POLYGON ((139.38 35.692, 139.39 35.691, 139.39...\n",
      "3      1319  POLYGON ((139.57 35.722, 139.57 35.724, 139.57...\n",
      "4      1322  POLYGON ((139.47 35.649, 139.47 35.647, 139.47...\n",
      "..      ...                                                ...\n",
      "69     1315  POLYGON ((139.8 35.617, 139.8 35.618, 139.79 3...\n",
      "70     1315  POLYGON ((139.77 35.62, 139.77 35.619, 139.79 ...\n",
      "71     1315  POLYGON ((139.84 35.631, 139.84 35.632, 139.83...\n",
      "72     1315  POLYGON ((139.82 35.64, 139.82 35.642, 139.82 ...\n",
      "73     1315  POLYGON ((139.84 35.703, 139.83 35.703, 139.83...\n",
      "\n",
      "[74 rows x 2 columns]\n"
     ]
    }
   ],
   "execution_count": 9
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:18:46.919600Z",
     "start_time": "2025-05-29T15:18:46.904454Z"
    }
   },
   "cell_type": "code",
   "source": [
    "from collections import defaultdict\n",
    "import numpy as np\n",
    "\n",
    "def generate_unique_integer_ids(n, min_value=1000, max_value=9999):\n",
    "    \"\"\"\n",
    "    生成唯一整数ID，保持4位数格式\n",
    "    :param n: 需要生成的ID数量\n",
    "    :param min_value: 最小ID值 (默认1000)\n",
    "    :param max_value: 最大ID值 (默认9999)\n",
    "    :return: 唯一整数ID数组\n",
    "    \"\"\"\n",
    "    # 计算可用ID范围\n",
    "    available_ids = set(range(min_value, max_value + 1))\n",
    "    \n",
    "    # 移除原始ID以避免冲突\n",
    "    existing_ids = set(gdf['tile_id'].unique())\n",
    "    available_ids -= existing_ids\n",
    "    \n",
    "    # 确保有足够ID\n",
    "    if len(available_ids) < n:\n",
    "        # 扩展范围（增加一位数）\n",
    "        new_min = min_value * 10\n",
    "        new_max = max_value * 10 + 9\n",
    "        additional_ids = set(range(new_min, new_min + (n - len(available_ids))))\n",
    "        available_ids |= additional_ids\n",
    "    \n",
    "    # 随机选择ID\n",
    "    return np.random.choice(list(available_ids), size=n, replace=False)\n",
    "\n",
    "\n",
    "n_rows = len(gdf)\n",
    "new_ids = generate_unique_integer_ids(n_rows)\n",
    "gdf['tile_id'] = new_ids"
   ],
   "id": "fce79d526d3c4197",
   "outputs": [],
   "execution_count": 14
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:18:49.789552Z",
     "start_time": "2025-05-29T15:18:49.776506Z"
    }
   },
   "cell_type": "code",
   "source": "print(gdf)",
   "id": "6a7e6fe3f69670a4",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    tile_id                                           geometry\n",
      "0      9248  POLYGON ((139.46 35.698, 139.46 35.7, 139.46 3...\n",
      "1      6907  POLYGON ((139.38 35.692, 139.37 35.692, 139.36...\n",
      "2      4423  POLYGON ((139.38 35.692, 139.39 35.691, 139.39...\n",
      "3      3700  POLYGON ((139.57 35.722, 139.57 35.724, 139.57...\n",
      "4      8947  POLYGON ((139.47 35.649, 139.47 35.647, 139.47...\n",
      "..      ...                                                ...\n",
      "69     8978  POLYGON ((139.8 35.617, 139.8 35.618, 139.79 3...\n",
      "70     2187  POLYGON ((139.77 35.62, 139.77 35.619, 139.79 ...\n",
      "71     4634  POLYGON ((139.84 35.631, 139.84 35.632, 139.83...\n",
      "72     2343  POLYGON ((139.82 35.64, 139.82 35.642, 139.82 ...\n",
      "73     6664  POLYGON ((139.84 35.703, 139.83 35.703, 139.83...\n",
      "\n",
      "[74 rows x 2 columns]\n"
     ]
    }
   ],
   "execution_count": 15
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:21:18.427079Z",
     "start_time": "2025-05-29T15:21:18.364225Z"
    }
   },
   "cell_type": "code",
   "source": [
    "pop_min = 80000\n",
    "pop_max = 3000000\n",
    "gdf['population'] = np.random.randint(pop_min, pop_max + 1, size=n_rows)"
   ],
   "id": "e209ca604817fc43",
   "outputs": [],
   "execution_count": 16
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-05-29T15:24:18.597674Z",
     "start_time": "2025-05-29T15:24:18.533457Z"
    }
   },
   "cell_type": "code",
   "source": [
    "output_path = \"../tessellation.geojson\"\n",
    "gdf.to_file(output_path, driver='GeoJSON')"
   ],
   "id": "7d3886fa8b021a4a",
   "outputs": [],
   "execution_count": 19
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
